"""
封装所有页面对象共性的操作，比如driver 的实例化
"""
import json
import logging
import time

import allure
import requests
from selenium.webdriver import Chrome, ActionChains, DesiredCapabilities

import yaml
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.remote.webdriver import WebDriver

from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC



class BasePage():

    # _base_url = None
    # 当子类没有构造函数的时候，在实例化的过程中，会自动父类的构造函数
    # 所以，每个PO 在实例化过程中，都会执行构造函数的逻辑
    # 问题： 如何避免driver 的重复实例化
    def __init__(self, base_driver=None):
        """
        告诉父类的构造函数，如果传参了，不需要进行重复的实例化操作
        如果没有传参， 那么就是第一次的实例化操作，需要进行实例化
        :param base_driver:
        """
        # 如果base_driver 为真， 为真就是不等于None，那么就不需要重复实例化的操作
        if base_driver:
            self.driver = base_driver
        # 如果base_driver 为None/假， 那么就需要对Driver进行实例化
        else:
            # 第一次实例化   本地运行
            #self.driver = webdriver.Chrome()

            chrome_options = Options()
            chrome_options.add_argument('--headless') # 浏览器不提供可视化界面。Linux下如果系统不支持可视化不加这条会启动失败
            chrome_options.add_argument('--disable-gpu')  # 谷歌文档提到需要加上这个属性来规避bug
            chrome_options.add_argument('blink-settings=imagesEnabled=false') # 不加载图片，提升运行速度
            chrome_options.add_argument('--hide-scrollbars') # 隐藏滚动条，应对一些特殊页面
            chrome_options.add_argument('--no-sandbox')  # 解决DevToolsActivePort文件不存在的报错

            # 最关键的一行  分布式静默模式运行

            self.driver = webdriver.Remote(options=chrome_options,command_executor="http://10.25.18.92:5001/wd/hub",desired_capabilities=DesiredCapabilities.CHROME) # 创建无界面对象


            if self._base_url != None:
                self.driver.get(self._base_url)
                self.driver.maximize_window()
                cookie = yaml.safe_load(open("../data/cookie.yaml"))
                if cookie[0]['expiry'] < round(time.time()):
                    logging.info(f'当前时间戳为:{round(time.time())}')
                    logging.info('当前文件cookie已过期')
                    self.open_yaml_element('../data/index.yaml','login_password',is_elements=1,ele_num=0,content='test')
                    self.open_yaml_element('../data/index.yaml','login_password',is_elements=1,ele_num=1,content='123456')
                    self.open_yaml_element('../data/index.yaml','login_click')
                    time.sleep(2)
                    cookie = self.driver.get_cookies()
                    with open('../data/cookie.yaml', 'w', encoding="utf-8") as f:
                        yaml.safe_dump(cookie, f)
                    logging.info('已重新获取有效cookies并写入')
                else:
                    logging.info('cookie有效期正常')
                    for c in cookie:
                        self.driver.add_cookie(c)
                    self.driver.get('http://172.16.10.21/mrpv2/#/doctorMemberHome')
                    time.sleep(2)

                self.driver.implicitly_wait(20)

            else:
                # 如果url 为空，那么抛出异常
                raise AttributeError

    def find(self, locator):
        logging.info(f"定位符：{locator[0]}, 定位表达式{locator[1]}")
        # logging.info(f"定位表达式{locator}")
        time.sleep(1)
        logging.info(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
        result: WebElement = WebDriverWait(self.driver, timeout=20).until(EC.element_to_be_clickable(locator))
        self.screen(locator)
        return result





    def elements(self, locator):
        logging.info(f"定位符：{locator[0]}, 定位表达式{locator[1]}")
        try:
            time.sleep(1)
            return self.driver.find_elements(*locator)
        except:
            return False
        result:WebElement = WebDriverWait(self.driver, 10).until(elements)
        return result

    def is_element(self, locator):
        try:
            self.find(locator)
            return True
        except:
            return False

    def is_elements(self, locator):
        try:
            self.elements(locator)
            time.sleep(1)
            return True
        except:
            return False


    def get_save_cookie(self):
        with open('cookies.yaml', 'w') as f:
            # 将cookies保存为json格式
            f.write(json.dumps(self.driver.get_cookies()))

    def add_cookie(self):
        self.driver = webdriver.Chrome()
        self.driver.get(self._base_url)
        self.driver.delete_all_cookies()
        with open('cookies.yaml', 'r') as f:
            # 使用json读取cookies 注意读取的是文件 所以用load而不是loads
            cookies_list = json.load(f)
            for cookie in cookies_list:
                self.driver.add_cookie(cookie)


    def elementclickintercepted(self,locator):
        time.sleep(2)
        selector = self.driver.find_element(*locator)
        self.driver.execute_script("arguments[0].click();",selector)


    def click_and_multi(self,locator_list):
        '''
        循环点击多个元素 locator_list:多个元素的列表
        :return:
        '''
        for ele in locator_list:
            self.find(ele).click()
            time.sleep(1)


    def open_yaml_element(self, file, key_name,content=None,is_elements=None,ele_num=None):
        # 读取yaml，取出关键数据，用parse解析
        '''

        :param file: 文件路径
        :param key_name: key_name
        :param content: 文本值
        :param is_elements:  是否是elements
        :param ele_num:   是elements时元素下标
        :return:
        '''
        with open(file, encoding="utf-8") as f:
            data = yaml.load(f)
            self.parse(data[key_name],content,is_elements,ele_num)

    def parse(self, func,content=None,is_elements=None,ele_num=None):   
        '''
        :param func: key值部分
        :param content: 文本值  非必填
        :param is_elements:  是否是elements
        :param ele_num:   是elements时元素下标
        :return:
        '''
        # 遍历每一个步骤
        for ele in func:
            # 如果操作是click就去点击
            if is_elements != None: # 当定位方式是elements时
                if "click" == ele["action"]:
                    self.elements((ele["by"], ele["locator"]))[ele_num].click()
                elif "send" == ele["action"]:
                    self.elements((ele["by"], ele["locator"]))[ele_num].send_keys(content)
            elif "click" == ele["action"]:  # 当action是click的时候
                try:
                    self.find((ele["by"], ele["locator"])).click()  # 去查找他的元素并且点击
                except:
                    try:
                        logging.info(f'定位器by:{ele["by"]}, locator:{ele["locator"]}用click()方法点击失败了!')
                        logging.info(f'定位器by:{ele["by"]}, locator:{ele["locator"]},更换为actionchains方法重试')
                        actions = ActionChains(self.driver)
                        actions.click(self.find((ele["by"], ele["locator"])))
                        logging.info(f'定位器by:{ele["by"]}, locator:{ele["locator"]},用actionchains重试成功了')
                    except:
                        self.driver.execute_script(f"document.querySelector('{ele['locator']}').click()")
            elif "send" == ele["action"]:
                self.find((ele["by"], ele["locator"])).send_keys(content)



    def only_read_yaml(self,file,key_name):
        with open(file, encoding="utf-8") as f:
            data = yaml.load(f)
            for i in data[key_name]:
                return i['by'],i['locator']

    def wait_for_next(self,locator):
        try:
            ele = self.find(locator)
            return ele
        except:
            logging.info(f'查找元素异常')
            logging.info(f'重新查找元素',locator)
            time.sleep(1)
            self.find(locator)
            self.wait_for_next(locator)

    def selector_execute_script(self, locator):
        '''
        locator : 是元素的value值 不需要传by定位方式
        :param locator:
        :return:
        '''
        try:
            time.sleep(1)
            self.driver.execute_script("document.querySelector('{}').click()".format(locator))
        except:
            time.sleep(1)
            logging.info(f'运行js locator:{locator}失败了，重试！')
            time.sleep(2)
            try:
                logging.info(f'再试一次{locator}')
                self.driver.execute_script("document.querySelector('{}').click()".format(locator))
                logging.info(f'locator:{locator}js点击成功')
            except:
                time.sleep(2)
                logging.info(f'再试一次{locator}')
                self.driver.execute_script("document.querySelector('{}').click()".format(locator))
                logging.info(f'locator:{locator}js点击成功')


    def read_test_data(self,file,key_name):
        with open(file, encoding="utf-8") as f:
            data = yaml.load(f)
            for i in data:
                return i[key_name]

    def scroll_find_element(self, ele):
        # 滚动查找元素
        return self.driver.execute_script("arguments[0].scrollIntoView();", ele)



    def verify_wait_click(self,locator):
        try:
            time.sleep(1)
            return self.find(locator)
        except:
            return False

        return WebDriverWait(self.driver, 10).until(verify_wait_click)




    def exist_readonly_view(self,remove_locator,start_locator,end_locator):
        '''
        设置开始 结束时间
        : locator :  .ant-input input:nth-child(1)  不包括By
        :return:
        '''

        remove_readonly = f"document.querySelector('{remove_locator}').removeAttribute('readonly')"
        self.driver.execute_script(remove_readonly)

        start_time = f"document.querySelector('{start_locator}').value='2021-12-30'"
        self.driver.execute_script(start_time)

        end_time = f"document.querySelector('{end_locator}').value='2022-02-09'"
        self.driver.execute_script(end_time)


    def screen(self,locator):
        # describe： allure 报告中截图的描述
        allure.attach(self.driver.get_screenshot_as_png(), f'{locator}', allure.attachment_type.PNG) #保存截图为allure的附件

class Base_Api():
    def __init__(self):
        self.host = 'http://172.16.10.21'

    def send(self, kwargs):
        r = requests.request(**kwargs)
        print(json.dumps(r.json(), indent=2, ensure_ascii=False))
        return r
